From 1e1412fd5bef18d8e7b84f6164dadd7229b8129f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 11 Jul 2014 08:35:23 -0700 Subject: [PATCH] Refactor Context into its own submodule --- src/cargo/ops/cargo_rustc/context.rs | 147 +++++++++++++++++++++++++++ src/cargo/ops/cargo_rustc/mod.rs | 114 ++++----------------- 2 files changed, 166 insertions(+), 95 deletions(-) create mode 100644 src/cargo/ops/cargo_rustc/context.rs diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs new file mode 100644 index 000000000..b8b0cee0f --- /dev/null +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -0,0 +1,147 @@ +use std::io::IoError; +use std::io; +use std::str; + +use core::{Package, PackageSet, Resolve, Target}; +use util; +use util::{CargoResult, ChainError, internal, Config}; + +pub struct Context<'a, 'b> { + pub deps_dir: Path, + pub primary: bool, + pub rustc_version: String, + pub config: &'b mut Config<'b>, + + dest: Path, + host_dylib: (String, String), + package_set: &'a PackageSet, + resolve: &'a Resolve, + target_dylib: (String, String), +} + +impl<'a, 'b> Context<'a, 'b> { + pub fn new(resolve: &'a Resolve, deps: &'a PackageSet, + config: &'b mut Config<'b>, + dest: Path, deps_dir: Path) -> CargoResult> { + let target_dylib = try!(Context::dylib_parts(config.target())); + let host_dylib = if config.target().is_none() { + target_dylib.clone() + } else { + try!(Context::dylib_parts(None)) + }; + Ok(Context { + rustc_version: try!(Context::rustc_version()), + dest: dest, + deps_dir: deps_dir, + primary: false, + resolve: resolve, + package_set: deps, + config: config, + target_dylib: target_dylib, + host_dylib: host_dylib, + }) + } + + /// Run `rustc` to figure out what its current version string is + fn rustc_version() -> CargoResult { + let output = try!(util::process("rustc").arg("-v").arg("verbose") + .exec_with_output()); + Ok(String::from_utf8(output.output).unwrap()) + } + + /// Run `rustc` to discover the dylib prefix/suffix for the target + /// specified. + fn dylib_parts(target: Option<&str>) -> CargoResult<(String, String)> { + let process = util::process("rustc") + .arg("-") + .arg("--crate-name").arg("-") + .arg("--crate-type").arg("dylib") + .arg("--print-file-name"); + let process = match target { + Some(s) => process.arg("--target").arg(s), + None => process, + }; + let output = try!(process.exec_with_output()); + + let output = str::from_utf8(output.output.as_slice()).unwrap(); + let parts: Vec<&str> = output.trim().split('-').collect(); + assert!(parts.len() == 2, "rustc --print-file-name output has changed"); + + Ok((parts.get(0).to_string(), parts.get(1).to_string())) + } + + /// Prepare this context, ensuring that all filesystem directories are in + /// place. + pub fn prepare(&self, pkg: &Package) -> CargoResult<()> { + debug!("creating target dir; path={}", self.dest.display()); + + try!(self.mk_target(&self.dest).chain_error(|| + internal(format!("Couldn't create the target directory for {} at {}", + pkg.get_name(), self.dest.display())))); + + try!(self.mk_target(&self.deps_dir).chain_error(|| + internal(format!("Couldn't create the directory for dependencies for {} at {}", + pkg.get_name(), self.deps_dir.display())))); + + Ok(()) + } + + fn mk_target(&self, target: &Path) -> Result<(), IoError> { + io::fs::mkdir_recursive(target, io::UserRWX) + } + + /// Switch this context over to being the primary compilation unit, + /// affecting the output of `dest()` and such. + pub fn primary(&mut self) { + self.primary = true; + } + + /// Return the destination directory for output. + pub fn dest<'a>(&'a self) -> &'a Path { + if self.primary {&self.dest} else {&self.deps_dir} + } + + /// Return the (prefix, suffix) pair for dynamic libraries. + /// + /// If `plugin` is true, the pair corresponds to the host platform, + /// otherwise it corresponds to the target platform. + fn dylib<'a>(&'a self, plugin: bool) -> (&'a str, &'a str) { + let pair = if plugin {&self.host_dylib} else {&self.target_dylib}; + (pair.ref0().as_slice(), pair.ref1().as_slice()) + } + + /// Return the exact filename of the target. + pub fn target_filename(&self, target: &Target) -> String { + let stem = target.file_stem(); + + if target.is_dylib() { + let (prefix, suffix) = self.dylib(target.get_profile().is_plugin()); + format!("{}{}{}", prefix, stem, suffix) + } else if target.is_rlib() { + format!("lib{}.rlib", stem) + } else { + unreachable!() + } + } + + /// For a package, return all targets which are registered as dependencies + /// for that package. + pub fn dep_targets(&self, pkg: &Package) -> Vec { + let deps = match self.resolve.deps(pkg.get_package_id()) { + None => return vec!(), + Some(deps) => deps, + }; + deps.map(|pkg_id| { + self.package_set.iter() + .find(|pkg| pkg_id == pkg.get_package_id()) + .expect("Should have found package") + }) + .filter_map(|pkg| { + pkg.get_targets().iter().find(|&t| { + t.is_lib() && t.get_profile().is_compile() + }) + }) + .map(|t| t.clone()) + .collect() + } +} diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 343f22d9d..619e4e127 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -1,34 +1,23 @@ use std::collections::HashMap; use std::hash::Hasher; use std::hash::sip::SipHasher; -use std::io::{File, IoError}; -use std::io; +use std::io::File; use std::os::args; -use std::str; use term::color::YELLOW; use core::{Package, PackageSet, Target, Resolve}; use util; -use util::{CargoResult, ChainError, ProcessBuilder, CargoError, internal, human}; +use util::{CargoResult, ProcessBuilder, CargoError, human}; use util::{Config, TaskPool, DependencyQueue, Fresh, Dirty, Freshness}; use self::job::Job; +use self::context::Context; mod job; +mod context; type Args = Vec; -struct Context<'a, 'b> { - dest: &'a Path, - deps_dir: &'a Path, - primary: bool, - rustc_version: &'a str, - resolve: &'a Resolve, - package_set: &'a PackageSet, - config: &'b mut Config<'b>, - dylib: (String, String) -} - // This is a temporary assert that ensures the consistency of the arguments // given the current limitations of Cargo. The long term fix is to have each // Target know the absolute path to the build location. @@ -62,42 +51,11 @@ pub fn compile_targets<'a>(env: &str, targets: &[&Target], pkg: &Package, .join(uniq_target_dest(targets).unwrap_or("")); let deps_target_dir = target_dir.join("deps"); - let output = try!(util::process("rustc").arg("-v").exec_with_output()); - let rustc_version = str::from_utf8(output.output.as_slice()).unwrap(); + let mut cx = try!(Context::new(resolve, deps, config, + target_dir, deps_target_dir)); // First ensure that the destination directory exists - debug!("creating target dir; path={}", target_dir.display()); - - try!(mk_target(&target_dir).chain_error(|| - internal(format!("Couldn't create the target directory for {} at {}", - pkg.get_name(), target_dir.display())))); - - try!(mk_target(&deps_target_dir).chain_error(|| - internal(format!("Couldn't create the directory for dependencies for {} at {}", - pkg.get_name(), deps_target_dir.display())))); - - let output = try!(util::process("rustc") - .arg("-") - .arg("--crate-name").arg("-") - .arg("--crate-type").arg("dylib") - .arg("--print-file-name") - .exec_with_output()); - - let output = str::from_utf8(output.output.as_slice()).unwrap(); - - let parts: Vec<&str> = output.slice_to(output.len() - 1).split('-').collect(); - assert!(parts.len() == 2, "rustc --print-file-name output has changed"); - - let mut cx = Context { - dest: &deps_target_dir, - deps_dir: &deps_target_dir, - primary: false, - rustc_version: rustc_version.as_slice(), - resolve: resolve, - package_set: deps, - config: config, - dylib: (parts.get(0).to_string(), parts.get(1).to_string()) - }; + try!(cx.prepare(pkg)); // Build up a list of pending jobs, each of which represent compiling a // particular package. No actual work is executed as part of this, that's @@ -118,8 +76,7 @@ pub fn compile_targets<'a>(env: &str, targets: &[&Target], pkg: &Package, try!(compile(targets.as_slice(), dep, &mut cx, &mut jobs)); } - cx.primary = true; - cx.dest = &target_dir; + cx.primary(); try!(compile(targets, pkg, &mut cx, &mut jobs)); // Now that we've figured out everything that we're going to do, do it! @@ -167,8 +124,8 @@ fn compile<'a>(targets: &[&Target], pkg: &'a Package, cx: &mut Context, // TODO: Can a fingerprint be per-target instead of per-package? Doing so // would likely involve altering the granularity of key for the // dependency queue that is later used to run jobs. - let fingerprint_loc = cx.dest.join(format!(".{}.fingerprint", - pkg.get_name())); + let fingerprint_loc = cx.dest().join(format!(".{}.fingerprint", + pkg.get_name())); let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx, targets)); @@ -219,19 +176,16 @@ fn fingerprint(package: String, profiles: u64) -> String { util::to_hex(hasher.hash(&(package, profiles))) } -fn mk_target(target: &Path) -> Result<(), IoError> { - io::fs::mkdir_recursive(target, io::UserRWX) -} - fn compile_custom(pkg: &Package, cmd: &str, cx: &Context) -> Job { // FIXME: this needs to be smarter about splitting let mut cmd = cmd.split(' '); let mut p = util::process(cmd.next().unwrap()) .cwd(pkg.get_root()) - .env("OUT_DIR", Some(cx.dest.as_str().expect("non-UTF8 dest path"))) - .env("DEPS_DIR", Some(cx.dest.join(cx.deps_dir) - .as_str().expect("non-UTF8 deps path"))) + .env("OUT_DIR", Some(cx.dest().as_str() + .expect("non-UTF8 dest path"))) + .env("DEPS_DIR", Some(cx.deps_dir.as_str() + .expect("non-UTF8 deps path"))) .env("TARGET", cx.config.target()); for arg in cmd { p = p.arg(arg); @@ -247,7 +201,7 @@ fn rustc(package: &Package, target: &Target, cx: &mut Context) -> Job { let root = package.get_root(); log!(5, "root={}; target={}; crate_types={}; dest={}; deps={}; verbose={}", - root.display(), target, crate_types, cx.dest.display(), + root.display(), target, crate_types, cx.dest().display(), cx.deps_dir.display(), cx.primary); let primary = cx.primary; @@ -306,7 +260,7 @@ fn build_base_args(into: &mut Args, into.push(crate_type.to_string()); } - let out = cx.dest.clone(); + let out = cx.dest().clone(); let profile = target.get_profile(); if profile.get_opt_level() != 0 { @@ -353,46 +307,16 @@ fn build_base_args(into: &mut Args, fn build_deps_args(dst: &mut Args, package: &Package, cx: &Context) { dst.push("-L".to_string()); - dst.push(cx.dest.display().to_string()); + dst.push(cx.dest().display().to_string()); dst.push("-L".to_string()); dst.push(cx.deps_dir.display().to_string()); - for target in dep_targets(package, cx).iter() { + for target in cx.dep_targets(package).iter() { dst.push("--extern".to_string()); dst.push(format!("{}={}/{}", target.get_name(), cx.deps_dir.display(), - target_filename(target, cx))); - } -} - -fn target_filename(target: &Target, cx: &Context) -> String { - let stem = target.file_stem(); - - if target.is_dylib() { - let (ref prefix, ref suffix) = cx.dylib; - format!("{}{}{}", prefix, stem, suffix) - } else if target.is_rlib() { - format!("lib{}.rlib", stem) - } else { - unreachable!() - } -} - -fn dep_targets(pkg: &Package, cx: &Context) -> Vec { - match cx.resolve.deps(pkg.get_package_id()) { - None => vec!(), - Some(deps) => deps - .map(|pkg_id| { - cx.package_set.iter() - .find(|pkg| pkg_id == pkg.get_package_id()) - .expect("Should have found package") - }) - .filter_map(|pkg| { - pkg.get_targets().iter().find(|&t| t.is_lib() && t.get_profile().is_compile()) - }) - .map(|t| t.clone()) - .collect() + cx.target_filename(target))); } } -- 2.30.2